home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
C++
/
Code Resources
/
Windows 95 MDEF
/
Sourcery
/
Windows95 MDEF.cpp
< prev
Wrap
C/C++ Source or Header
|
1996-06-12
|
21KB
|
654 lines
/*
Windows95 MDEF Emulator
Version 2.0
Evolved from the NeXT MDEF
written on Jan. 17, 1994 (Right after the Northridge earthquake—whew!!)
by Hiep Dam, From The Witches' Brew
Contact: America Online -> StarLabs
Internet -> starlabs@aol.com
FREE! FREE! FREE!
This code & MDEF are in the public domain.
Usage:
Default resource id of MDEF: 1972
Go into ResEdit and edit the menu's MDEF ID to 1972. That's it!
If you just use the MDEF by itself, by default the menus will have
a nice 3D grayish look. Your average 3D color customized look...
Nice, but boring to some.
...
However, things get more exciting if you add custom resources.
The MDEF looks for any resources of type 'MnuT' ("Menu template")
that are the same resource id as the menu. If it doesn't find any
then it looks for one with the default id of 0. Barring that it
uses the default look mentioned above.
The 'MnuT' resources define how the Windows95 MDEF colors and draws
the menu. You specify 3 RGB colors: the menu background color, the
menu hilite (light) color, and the menu shadow (dark) color. The
shadow color will also be used to hilite the selected menu item for
that menu. You must also specify in the 'MnuT' resource the menu's
font, font size, and font face.
The use of 'MnuT' makes the Windows95 MDEF very versatile and gives
you control over the color scheme and font appearance of your
menus. Note: 'mctb' resources are not supported; they're
really too overblown for my tastes.
Notes:
What this MDEF supports:
* Menu items as submenu items (i.e. points to a submenu w/ a triangle)
* as a submenu itself
* cmd-keys
* item marks
* item text styles
What this MDEF does not support:
* meta-characters and parsing menu item text for such characters
* icons, sicns, reduced icons, cicns. In other words, no icons.
* scrolling. If you have a lot of menu items, the ones near the
bottom of the screen will be clipped. Don't use too many items!
1) This MDEF makes several IMPORTANT *assumptions* about the environment
it's running on. Running in an incorrect environment will result
in messy crashes.
a) Color Quickdraw is present. It uses RGBColors to draw the menu,
not the old-style 8-color Quickdraw model. There is no check
to see if Color Quickdraw is available or not. If the MDEF is
running in a b&w environment, there will be an "unimplemented
trap" error. The MDEF absolutely requires Color QD.
b) System 7 is present.
2) The original portions of the NeXT MDEF were derived from an example MDEF
included in THINK Reference. See "Custom Menus" in the Menu Manager
section. The code has changed significantly with the move over to
a Windows95 look from the NeXT look.
3) I think this code is a good place to start when writing your own
customized MDEFs. Note that to write MDEFs which support more than just
the most rudimentary stuff (i.e. more than plain text) things can get
hairy pretty fast. You have to take into account submenus, item
marks, command keys, large icons, small icons, and so on. And if
you want to support extensions such as other modifier keys like
control, shift, and option keys; whew, it's a lot of work. I
don't envy the author of the Mercutio MDEF...
Version History:
1.0: Initial release (limited to a source code CD-ROM); was NeXT MDEF
2.0: 11/28/95
Modified mdef -> now Windows95 MDEF. Boxy menu item look removed
(admittedly it had looked rather tacky).
Pretty much complete overhaul. Gotta say it: my old code was
pretty messy...
2.0.1: 12/1/95
Fixed submenu problem partially by using mbSaveLoc. What a
pain! I have to muck around low-memory system globals JUST
TO MAKE THE DARN THING WORK! Most of the stuff can be found
in the mChooseMsg handler.
Also "fixed" popup menu problem using a sample MDEF written
by Ken Worley. Thanks Ken! (The popup menu still might pop
up incorrectly if the system 7 popup menu cdef is used and
the menu is placed near the edges of the monitor. I figure
it's the system's fault - not mine). Stuff can be found in
the mPopUpMsg handler.
*/
// ---------------------------------------------------------------------------
#include "DrawMenuItem.h"
#include "GetDominantDevice.h"
// ---------------------------------------------------------------------------
/*
Writing an MDEF which properly uses submenus (among other things)
requires that we mess with undocumented system globals, a practice
that will probably blow up in our faces in the future. Set to
0 when that time comes, recompile, and voila! Problems should then
be fixed if we have written the MDEF well.
*/
#define USE_UNDOCUMENTED_STUFF 1
// ---------------------------------------------------------------------------
// Some prototypes
pascal void main(short msg, MenuHandle whichMenu, Rect *menuRect,
Point hitPt, short *itemID);
static void FillInColor(
RGBColor *theColor,
unsigned short r,
unsigned short g,
unsigned short b);
static void InitMenuData(short menuID, MDEFstuff *data);
static short GetMenuItemHeight();
static short GetMenuHeight(short numItems);
static short FindMenuItem(MenuHandle whichMenu, Rect *menuRect, Point hitPt);
static short GetMenuWidth(MenuHandle whichMenu, short numItems, MDEFstuff *mdefData);
static void DoSizeMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData);
static void DoPopUpMsg(
MenuHandle whichMenu,
Rect *menuRect,
short menuItem,
Point hitPt,
MDEFstuff *mdefData);
static void DoDrawMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData);
static void DoChooseMsg(
MenuHandle whichMenu,
Rect *menuRect,
Point hitPt,
short *itemID,
MDEFstuff *mdefData);
// ----------------------------------------------------------------------
/*
Main.
This is the entrypoint for the MDEF. So what does that mean? Simply
this is the function that will be called when the MDEF is used.
It checks what is the current message sent to it, case goes
thru a switch statement to find the correct handler for the msg.
*/
pascal void main(short msg, MenuHandle whichMenu, Rect *menuRect, Point hitPt,
short *theMenuItem) {
MDEFstuff menuData;
GrafPtr curPort;
short saveFont, saveSize, saveFace, saveMode;
RGBColor saveFore, saveBack;
PenState savePen;
// We're implicitly drawing into the Window Manager port, so make
// sure we save the important settings so we can restore them later on.
GetPort(&curPort);
saveFont = curPort->txFont;
saveSize = curPort->txSize;
saveFace = curPort->txFace;
saveMode = curPort->txMode;
GetForeColor(&saveFore);
GetBackColor(&saveBack);
GetPenState(&savePen);
InitMenuData((**whichMenu).menuID, &menuData);
TextFont(menuData.params.menuFont);
TextSize(menuData.params.menuSize);
TextFace(menuData.params.menuFace);
switch (msg) {
case mDrawMsg: {
DoDrawMsg(whichMenu, menuRect, &menuData);
} break;
case mChooseMsg: {
DoChooseMsg(whichMenu, menuRect, hitPt, theMenuItem, &menuData);
} break;
case mSizeMsg: {
DoSizeMsg(whichMenu, menuRect, &menuData);
} break;
case mPopUpMsg: {
DoPopUpMsg(whichMenu, menuRect, *theMenuItem, hitPt, &menuData);
} break;
} // END switch
// Polite manners: if we changed the font, restore the system font
// upon exiting...
TextFont(saveFont);
TextSize(saveSize);
TextFace(saveFace);
TextMode(saveMode);
RGBForeColor(&saveFore);
RGBBackColor(&saveBack);
SetPenState(&savePen);
} // END main
// ----------------------------------------------------------------------
/*
IsItemDisabled.
Finds out whether the given menu item is disabled or not. Note though
that we also have to check if the *entire* menu is disabled or not, in
addition to the menu item. We do this by checking bit 0 of the
enableFlags field in the menuInfo structure of a menu.
This is done because the menu can be disabled regardless whether
the menu item is enabled or not - the menu takes precedence over
the menu items.
With the advent of Copland, this part will be incompatible. Hopefully
accessors for a menu item's enabled/disabled state will be available.
*/
Boolean IsItemDisabled(MenuHandle whichMenu, short whichItem) {
if (whichItem > 31)
return(false); // Items > 31 always enabled
Str255 itemText;
GetItem(whichMenu, whichItem, itemText);
if (itemText[1] == kDividerChar)
return(true); // Divider menu items always disabled.
return(!BitTst(&(**whichMenu).enableFlags, 31 - whichItem) ||
!BitTst(&(**whichMenu).enableFlags, 31 - 0));
} // END IsItemDisabled
// ----------------------------------------------------------------------
/*
GetMenuItemHeight.
Get the height of any single menu item. If you wish to add icons
to your menu, you'll have to change this to accomodate larger icons.
This simply calls _GetFontInfo.
*/
short GetMenuItemHeight() {
FontInfo theInfo;
GetFontInfo(&theInfo);
// Account for padding for both top and bottom (that's why we're *2)
return(theInfo.ascent + (kHeightPadding + kHeightPadding));
} // END GetMenuItemHeight
// ----------------------------------------------------------------------
/*
GetMenuHeight.
Gets the height of a single menu item, and finds the height
of the whole menu by multiplying a single menu item height
by the number of menu items.
*/
short GetMenuHeight(short numItems) {
return((numItems * GetMenuItemHeight()) + (kRectPadding + kRectPadding));
} // END GetMenuHeight
// ----------------------------------------------------------------------
/*
GetMenuWidth.
Gets the width of the entire menu, by finding the width of the
widest menu item in the menu. Polls each menu item for its width,
keeping track of the largest width.
Accounts for submenu "triangles" mini-icon and cmd-keys in menu
item width.
*/
short GetMenuWidth(MenuHandle whichMenu, short numItems, MDEFstuff *mdefData) {
Str255 itemText;
short maxLength = 0;
short curLength = 0;
short theChar;
short cmdWidth;
short checkWidth;
Style style;
Boolean hasCmdKeys = false;
checkWidth = CharWidth(kCheckMarkChar) +
kItemMarkPadding + kItemMarkPadding; // 4 = left & right padding
cmdWidth = CharWidth(kCmdKeyChar); // "Cmd" key clover character
cmdWidth += CharWidth(kWidestChar); // "W" is widest character in the bunch
for (short i = 1; i <= numItems; i++) {
GetItem(whichMenu, i, itemText);
GetItemStyle(whichMenu, i, &style);
if (style)
TextFace(style);
else
TextFace((mdefData->params).menuFace);
curLength = StringWidth(itemText);
GetItemCmd(whichMenu, i, &theChar);
if (theChar != 0)
hasCmdKeys = true;
curLength += checkWidth;
if (curLength > maxLength)
maxLength = curLength;
}
// Padding is both to left and right of cmdKey+char itself.
if (hasCmdKeys)
maxLength += (cmdWidth + (kCmdKeyPadding + kCmdKeyPadding));
return(maxLength + (kWidthPadding + kWidthPadding));
} // END GetMenuWidth
// ----------------------------------------------------------------------
/*
GetMenuItemRect.
Determine rect of menu item. Pretty obvious.
*/
void GetMenuItemRect(Rect *menuRect, Rect *itemRect, short whichItem) {
short oneHeight = GetMenuItemHeight();
//InsetRect(menuRect, kRectPadding, kRectPadding);
itemRect->left = menuRect->left;
itemRect->right = menuRect->right;
itemRect->top = menuRect->top + (oneHeight * (whichItem - 1));
itemRect->bottom = itemRect->top + oneHeight;
//InsetRect(menuRect, -kRectPadding, -kRectPadding);
} // END GetMenuItemRect
// ---------------------------------------------------------------------------
// FindMenuItem.
// Given a point, find if this point is within the rect of any menu
// item. If so, return the item, else 0.
short FindMenuItem(MenuHandle whichMenu, Rect *menuRect, Point hitPt) {
Rect itemRect;
short itemCount = CountMItems(whichMenu);
if (!PtInRect(hitPt, menuRect))
return(0);
for (short i = 1; i <= itemCount; i++) {
GetMenuItemRect(menuRect, &itemRect, i);
if (PtInRect(hitPt, &itemRect)) {
return(i);
}
}
return(0);
} // END FindMenuItem
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
/*
DoSizeMsg.
Even more obvious (más o menos). Calls others to do the job.
*/
void DoSizeMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData) {
short itemCount = CountMItems(whichMenu);
(**whichMenu).menuWidth = GetMenuWidth(whichMenu, itemCount, mdefData);
(**whichMenu).menuHeight = GetMenuHeight(itemCount);
} // END DoSizeMsg
// ----------------------------------------------------------------------
enum {
kPopupMargin = 4
};
void DoPopUpMsg(
MenuHandle whichMenu,
Rect *menuRect,
short menuItem,
Point hitPt,
MDEFstuff *mdefData) {
/*
Note how hitPt.h and hitPt.v are matched to top and left.
Not intuitive! Another quirk in the OS.
*/
short itemCount = CountMItems(whichMenu);
menuRect->top = hitPt.h - (menuItem * GetMenuItemHeight());
menuRect->left = hitPt.v;
// Get the width & height
DoSizeMsg(whichMenu, menuRect, mdefData);
menuRect->right = menuRect->left + (**whichMenu).menuWidth;
menuRect->bottom = menuRect->top + (**whichMenu).menuHeight;
Rect deviceBounds;
deviceBounds = (**GetDominantDevice(menuRect)).gdRect;
if (menuRect->bottom > (deviceBounds.bottom-kPopupMargin))
OffsetRect(menuRect, 0, (deviceBounds.bottom-kPopupMargin) - menuRect->bottom);
if (menuRect->right > (deviceBounds.right-kPopupMargin))
OffsetRect(menuRect, (deviceBounds.right-kPopupMargin) - menuRect->right, 0);
if (menuRect->top < (deviceBounds.top+kPopupMargin))
OffsetRect(menuRect, 0, (deviceBounds.top+kPopupMargin) - menuRect->top);
if (menuRect->left < (deviceBounds.left+kPopupMargin))
OffsetRect(menuRect, (deviceBounds.left+kPopupMargin) - menuRect->left, 0);
} // END DoPopUpMsg
// ---------------------------------------------------------------------------
/*
DoDrawMsg.
Erases the entire menu, and calls each menu item individually to
draw itself.
*/
void DoDrawMsg(MenuHandle whichMenu, Rect *menuRect, MDEFstuff *mdefData) {
short itemCount = CountMItems(whichMenu);
RGBBackColor(&(mdefData->params).menuBkgndColor);
EraseRect(menuRect);
PenSize(1, 1);
InsetRect(menuRect, 1, 1);
RGBForeColor(&(mdefData->params).menuHiliteColor);
MoveTo(menuRect->left, menuRect->top);
LineTo(menuRect->right, menuRect->top);
MoveTo(menuRect->left, menuRect->top);
LineTo(menuRect->left, menuRect->bottom);
RGBForeColor(&(mdefData->params).menuShadowColor);
MoveTo(menuRect->right, menuRect->bottom);
LineTo(menuRect->right, menuRect->top);
MoveTo(menuRect->right, menuRect->bottom);
LineTo(menuRect->left, menuRect->bottom);
for (short i = 1; i <= itemCount; i++) {
DrawMenuItem(whichMenu, menuRect, i, kMenuUnhilited, mdefData);
}
InsetRect(menuRect, -1, -1);
} // END DoDrawMsg
// ----------------------------------------------------------------------
#pragma mark UNDOCUMENTED DATA!
#if USE_UNDOCUMENTED_STUFF
/*
Define some undocumented low-memory system globals. You can bet
yer dollar this will break in future releases of the MacOS, but
doing this is a necessary evil, as it is required to make the
MDEF work __properly__ with submenus.
*/
// mbSaveLoc, mbItemRect, and mbUglyScroll stuff
typedef struct {
short lastMBSave;
long mbCustomStorage;
Rect mbItemRect; // Greatest item of interest to us currently
char mbMenuDelay;
char mbMenuDrag;
short mbUglyScroll; // Of semi-interest (tho what it does I don't know)
short mbIconState;
long mbHeader; // Actually, size is unknown!
} MBGlobalData;
#define mbItemRectOffset 6
#define mbUglyScrollOffset 16
#define sgMBSaveLoc *((Handle*)0x0B5C)
#define sgMBItemRectPtr ((Rect*)((*sgMBSaveLoc) + mbItemRectOffset))
#define sgMBUglyScrollPtr ((short*)((*sgMBSaveLoc) + mbUglyScrollOffset))
#define sgHuhRectPtr ((Rect*)0x09FA)
// menuDisable stuff
typedef struct {
short hiWord;
short loWord;
} StuffedLong;
#define sgMenuDisablePtr ((StuffedLong*)0x0B54)
#endif // USE_UNDOCUMENTED_STUFF
// ---------------------------------------------------------------------------
/*
DoChooseMsg.
This is the only other function which calls DrawMenuItem, other than
DoDrawMsg. Takes care of the hiliting/unhiliting of menu items.
*/
void DoChooseMsg(
MenuHandle whichMenu,
Rect *menuRect,
Point hitPt,
short *itemID,
MDEFstuff *mdefData) {
Str255 itemText;
short mouseItem;
mouseItem = FindMenuItem(whichMenu, menuRect, hitPt);
if (mouseItem != 0) {
// Pre-fetch some data we'll need later on (item text)
GetItem(whichMenu, mouseItem, itemText);
#if USE_UNDOCUMENTED_STUFF
/*
Boo-hoo-hoo! We need to set the mbItemRect stored in mbSaveLoc.
All UNDOCUMENTED, but REQUIRED if you wish to make your mdef
work properly with submenus. Thank God for Usenet and sample
source code! If this is taken out, the submenus flash about
5 times, cause they get the mDrawMsg 5 times for some reason!
Thus they erase, redraw 5 times, causing significant flicker.
Yuck.
*/
Rect itemRect;
GetMenuItemRect(menuRect, &itemRect, mouseItem);
*sgMBItemRectPtr = itemRect;
/*
Either using the below lines or commenting them out doesn't
seem to make any difference so far. So the less we muck
with the system the safer we'll be. Uncomment when the
purpose(s) are found...
*/
// *sgMBUglyScrollPtr = true;
// *sgHuhRectPtr = itemRect; // MenuSelect seems to be needing this
#endif // USE_UNDOCUMENTED_STUFF
}
InsetRect(menuRect, 1, 1);
if (mouseItem == 0 || // out of bounds or disabled
IsItemDisabled(whichMenu, mouseItem)) {
DrawMenuItem(whichMenu, menuRect, *itemID, kMenuUnhilited, mdefData);
*itemID = 0; // return "cancel" code
}
else if (mouseItem != *itemID) {
// unhilight previous
DrawMenuItem(whichMenu, menuRect, *itemID, kMenuUnhilited, mdefData);
// hilight new
DrawMenuItem(whichMenu, menuRect, mouseItem, kMenuHilited, mdefData);
// return new
*itemID = mouseItem;
}
#if USE_UNDOCUMENTED_STUFF
// Set the MenuDisable low-mem global; whether the
// item is disabled or not doesn't matter.
sgMenuDisablePtr->hiWord = (**whichMenu).menuID;
sgMenuDisablePtr->loWord = mouseItem;
#endif // USE_UNDOCUMENTED_STUFF
InsetRect(menuRect, -1, -1);
} // END DoChooseMsg
// ----------------------------------------------------------------------
void InitMenuData(short menuID, MDEFstuff *data) {
MDEFstuff **menuParameters;
FillInColor(&data->white, kWhite, kWhite, kWhite);
FillInColor(&data->black, kBlack, kBlack, kBlack);
// If an optional 'MnuT' resource exists, which describes the
// menu color and font usage, load that. Else fill in default
// values for MDEF.
menuParameters = (MDEFstuff**)GetResource(kDefaultMDEFparametersType,
menuID);
if (menuParameters == NULL) {
// Can't find an 'MnuT' with same id as menu. Try default 'MnuT' rsrc id...
menuParameters = (MDEFstuff**)GetResource(
kDefaultMDEFparametersType, kDefaultMDEFparametersID);
}
if (menuParameters == NULL) {
(data->params).menuHiliteColor = data->white;
FillInColor(&(data->params).menuBkgndColor, kLtGray, kLtGray, kLtGray);
FillInColor(&(data->params).menuShadowColor, kDkGray, kDkGray, kDkGray);
(data->params).menuSelectionColor = (data->params).menuShadowColor;
(data->params).menuFont = kMenuFont;
(data->params).menuSize = kMenuSize;
(data->params).menuFace = kMenuFace;
(data->params).exactWin95Look = false;
}
else {
HLock((Handle)menuParameters);
BlockMove(*menuParameters, &data->params, sizeof(MDEFparameters));
HUnlock((Handle)menuParameters);
ReleaseResource((Handle)menuParameters);
}
} // END InitMenuData
// ---------------------------------------------------------------------------
/*
FillInColor.
If you use RGBColors, no doubt you'll be using some function similar to this.
Now why didn't Apple include a function like this? Less overhead, I guess.
Apple should also have included get/set routines for a grafport's txFont,
txSize, txFace, and txMode. But who's perfect?
*/
void FillInColor(
RGBColor *theColor,
unsigned short r,
unsigned short g,
unsigned short b) {
theColor->red = r;
theColor->green = g;
theColor->blue = b;
} // END FillInColor
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// END Windows95 MDEF.cpp